using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using Game500.ClientServerCommon.Src;
using Game500.ClientServerCommon.Src.Networking;
using Game500.Server;
using ProtoBuf;

namespace Game500.StServer.Src
{
  public class NetworkPlayer
  {
    private readonly List<GamerAction> myActions = new List<GamerAction>();
    private readonly NetworkPacketStream myNetworkPacketStream;
    private readonly TcpClient myTcpClient;
    private bool myIsConnected = true;
    private string myUsername;

    public NetworkPlayer(TcpClient tcpClient)
    {
      myTcpClient = tcpClient;
      myNetworkPacketStream = new NetworkPacketStream(tcpClient.GetStream());
      myNetworkPacketStream.ErrorEvent += ErrorHandler;
      myNetworkPacketStream.DataReceivedEvent += DataReceivedHandler;
      myNetworkPacketStream.StartListen();
    }

    public Client CoreId { get; set; }

    public string Username
    {
      get { return myUsername; }
      set { myUsername = value; }
    }

    public event PlayerLeftDelegate PlayerDisconnected;
    public event PlayerJoinedDelegate PlayerJoined;

    private void UpdateInfo(GamerInfo info)
    {
      myUsername = info.Username;
    }

    private void DataReceivedHandler(NetworkPacket packet)
    {
      using (var stream = new MemoryStream(packet.InnerData))
      {
        switch (packet.Type)
        {
          case NetworkPacketType.GamerActionPacket:
            var action = Serializer.Deserialize<GamerAction>(stream);
            lock (myActions)
            {
              myActions.Add(action);
            }
            break;
          case NetworkPacketType.GamerInfoPacket:
            var gamerInfo = Serializer.Deserialize<GamerInfo>(stream);
            UpdateInfo(gamerInfo);
            RaisePlayerJoinedEvent();
            break;
        }
      }
    }

    private void ErrorHandler(Exception exception)
    {
      RaisePlayerDisconnectedEvent();
    }

    protected void RaisePlayerDisconnectedEvent()
    {
      if (PlayerDisconnected != null && myIsConnected)
      {
        PlayerDisconnected(this);
      }
      myIsConnected = false;
    }

    protected void RaisePlayerJoinedEvent()
    {
      if (PlayerJoined != null)
      {
        PlayerJoined(this);
      }
    }

    public IEnumerable<GamerAction> GetActions()
    {
      lock (myActions)
      {
        return new List<GamerAction>(myActions);
      }
    }

    public void ResetActions()
    {
      lock (myActions)
      {
        myActions.Clear();
      }
    }

    public bool SendFrame(SceneFrame frame)
    {
      using (var stream = new MemoryStream())
      {
        Serializer.Serialize(stream, frame);
        NetworkPacket packet = NetworkPacket.CreatePacket(NetworkPacketType.SceneFramePacket, stream.GetBuffer(), (int)stream.Length);
        return myNetworkPacketStream.TrySendNetworkPacket(packet);
      }
    }

    public void SendGameInfo(int screenWidth, int screenHeigth, Guid mapId)
    {
      using (var stream = new MemoryStream())
      {
        var gameInfo = new GameInfo {PlayerId = CoreId.ID, SceneWidth = screenWidth, SceneHeight = screenHeigth, MapId = mapId};
        Serializer.Serialize(stream, gameInfo);
        NetworkPacket packet = NetworkPacket.CreatePacket(NetworkPacketType.GameInfoPacket, stream.GetBuffer(), (int)stream.Length);
        myNetworkPacketStream.TrySendNetworkPacket(packet);
      }
    }

    public void SendPlayerLimitReached(int limit)
    {
      var limitEvent = new PlayerLimitReachedEvent {MaxPlayers = limit};
      using (var stream = new MemoryStream())
      {
        Serializer.Serialize(stream, limitEvent);
        NetworkPacket packet = NetworkPacket.CreatePacket(NetworkPacketType.PlayerLimitReachedPacket, stream.GetBuffer(), (int)stream.Length);
        myNetworkPacketStream.SendPacket(packet);
      }
    }

    public void SendExplosions(ExplosionsEvent exp)
    {
      using (var stream = new MemoryStream())
      {
        Serializer.Serialize(stream, exp);
        NetworkPacket packet = NetworkPacket.CreatePacket(NetworkPacketType.ExplosionEventPacket, stream.GetBuffer(), (int)stream.Length);
        myNetworkPacketStream.SendPacket(packet);
      }
    }

    public void SendShots(ShotsEvent shots)
    {
      using (var stream = new MemoryStream())
      {
        Serializer.Serialize(stream, shots);
        NetworkPacket packet = NetworkPacket.CreatePacket(NetworkPacketType.ShotEventPacket, stream.GetBuffer(), (int)stream.Length);
        myNetworkPacketStream.SendPacket(packet);
      }
    }
  }
}